package com.admin.qujiang.util;


import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.beanutils.ConvertUtils;
import org.apache.commons.beanutils.converters.*;
import org.apache.commons.lang.StringUtils;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.text.DecimalFormat;
import java.text.NumberFormat;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * 数据工具类
 * 主要用于获取特定信息、转化数据结构等
 * Created by ht on 2018/5/28.
 *
 * @author ht
 */
public class DataUtils {
    //默认除法运算精度
    private static final int DEF_DIV_SCALE = 6;

    /**
     * 由Map反射到Bean，忽略大小写
     *
     * @param map  目标map
     * @param bean 需要返回的Bean类型
     */
    @SuppressWarnings("unchecked")
    public static <T> T reflectBean(Map<String, ? extends Object> map, Class<T> bean) {
        Object targetBean;
        try {
            targetBean = ((Class) bean).newInstance();
            Method[] methods = bean.getMethods();
            for (Method method : methods) {
                //找到set方法
                if (method.getName().startsWith("set")) {
                    String key = method.getName().replace("set", "");
                    key = key.toUpperCase();
                    Object value = map.get(key);
                    if (value == null || "N/A".equals(value)) {
                        continue;
                    }
                    Class<?>[] paramType = method.getParameterTypes();
                    //根据参数类型执行对应的set方法给vo赋值
                    if (paramType[0] == String.class) {
                        method.invoke(targetBean, String.valueOf(value));
                    } else if (paramType[0] == int.class || paramType[0] == Integer.class) {
                        method.invoke(targetBean, Integer.valueOf(value.toString()));
                    } else if (paramType[0] == long.class || paramType[0] == Long.class) {
                        method.invoke(targetBean, Long.valueOf(value.toString()));
                    } else if (paramType[0] == double.class || paramType[0] == Double.class) {
                        method.invoke(targetBean, Double.parseDouble(value.toString()));
                    } else if (paramType[0] == boolean.class || paramType[0] == Boolean.class) {
                        method.invoke(targetBean, Boolean.parseBoolean(value.toString()));
                    } else if (paramType[0] == char.class || paramType[0] == Character.class) {
                        method.invoke(targetBean, value.toString().charAt(0));
                    } else if (paramType[0] == Date.class) {
                        method.invoke(targetBean, (Date) value);
                    } else if (paramType[0] == BigDecimal.class) {
                        method.invoke(targetBean, new BigDecimal(value.toString()));
                    }
                }
            }
        } catch (InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
            throw new OperateException(map + "由Map反射到Bean,转换失败");
        }
        return (T) targetBean;
    }

    /**
     * bean转换
     *
     * @param bean       待转换bean
     * @param properties map
     */
    public static void populate(Object bean, Map<String, ? extends Object> properties) {
        //注册转换器
        ConvertUtils.register(new LongConverter(null), Long.class);
        ConvertUtils.register(new ShortConverter(null), Short.class);
        ConvertUtils.register(new IntegerConverter(null), Integer.class);
        ConvertUtils.register(new DoubleConverter(null), Double.class);
        ConvertUtils.register(new BigDecimalConverter(null), BigDecimal.class);
        ConvertUtils.register(new DateConverter(null), Date.class);
        try {
            BeanUtils.populate(bean, properties);
        } catch (IllegalAccessException | InvocationTargetException e) {
            e.printStackTrace();
            throw new OperateException(properties + "BeanUtil转换失败");
        }
    }

//    public static List<Map> popList(List<? extends Object> beanList) {
//        List<Map> result = new ArrayList<>();
//        beanList.forEach(t -> {
//            Map<String, ? extends Object> map = new HashMap<>();
//            populate(t, map);
//            result.add(map);
//        });
//        return result;
//    }

    /**
     * bean转换
     *
     * @param properties map
     * @param c          转换的类型
     * @param <T>        泛型
     */
    public static <T> T populate(T t, Map<String, ? extends Object> properties, Class<T> c) {
        try {
            t = c.newInstance();
        } catch (InstantiationException | IllegalAccessException e) {
            e.printStackTrace();
            throw new OperateException(properties + "构造失败");
        }
        populate(t, properties);
        return t;
    }

    /**
     * List按照Map中的的某个field排序
     *
     * @param dataList 数据列表
     * @param field    字段
     */
    public static void sortListByField(List<Map> dataList, String field) {
        //对list进行重新按照field进行升序-从小到大
        if (null != dataList && dataList.size() > 0) {
            //也可以使用Collections.sort
            dataList.sort((firstMap, secondMap) -> {
                int compare;
                Object value1 = firstMap.get(field);
                Object value2 = secondMap.get(field);

                if (value1 == null && value2 != null) {
                    return 1;
                } else if (value1 != null && value2 == null) {
                    return -1;
                } else if (value1 == null) {
                    return 0;
                }

                //比较两个对象的顺序，如果前者小于、等于或者大于后者，则分别返回-1/0/1
                //逆序的话就用o2.compareTo(firstMap)即可
                compare = value1.toString().compareTo(value2.toString());
                return compare;
            });
        }
    }

    /**
     * 获取Map中指定字段值
     *
     * @param dataMap Map
     * @param field   字段
     * @param <T>     需要返回的类型
     * @return Map中指定字段值
     */
    @SuppressWarnings("unchecked")
    public static <T> T getMapField(Map dataMap, String field) {
        return dataMap.get(field) == null ? null : (T) String.valueOf(dataMap.get(field));
    }

    /**
     * double类型保留两位小数
     *
     * @param number 需要格式化的数字
     * @return 保留两位小数的double
     */
    public static Double getTwoDecimal(Double number) {
        DecimalFormat formatter = new DecimalFormat("#.00");
        return Double.valueOf(formatter.format(number));
    }

    /**
     * double类型保留指定位数小数
     *
     * @param number 需要格式化的数字
     * @param digits 位数
     * @return 保留两位小数的double
     */
    public static String formatNumberByDigits(Double number, Integer digits) {
        NumberFormat format = NumberFormat.getNumberInstance();
        format.setMaximumFractionDigits(digits);
        String strVale = format.format(number);
        int dot = strVale.lastIndexOf(',');
        if ((dot > -1) && (dot <= (strVale.length() - 1))) {
            strVale = strVale.replace(",", "");
        }
        return strVale;
    }

    /**
     * 提供精确的加法运算。
     *
     * @param value1 被加数
     * @param value2 加数
     * @return 两个参数的和
     */
    public static double add(double value1, double value2) {
        BigDecimal b1 = new BigDecimal(Double.toString(value1));
        BigDecimal b2 = new BigDecimal(Double.toString(value2));
        return b1.add(b2).doubleValue();
    }

    /**
     * 提供精确的减法运算。
     *
     * @param value1 被减数
     * @param value2 减数
     * @return 两个参数的差
     */
    public static double sub(double value1, double value2) {
        BigDecimal b1 = new BigDecimal(Double.toString(value1));
        BigDecimal b2 = new BigDecimal(Double.toString(value2));
        return b1.subtract(b2).doubleValue();
    }

    /**
     * 提供精确的乘法运算。
     *
     * @param value1 被乘数
     * @param value2 乘数
     * @return 两个参数的积
     */
    public static double multiply(double value1, double value2) {
        BigDecimal b1 = new BigDecimal(Double.toString(value1));
        BigDecimal b2 = new BigDecimal(Double.toString(value2));
        return b1.multiply(b2).doubleValue();
    }

    /**
     * 提供（相对）精确的除法运算，当发生除不尽的情况时，精确到
     * 小数点以后10位，以后的数字四舍五入。
     *
     * @param value1 被除数
     * @param value2 除数
     * @return 两个参数的商
     */
    public static double divide(double value1, double value2) {
        return divide(value1, value2, DEF_DIV_SCALE);
    }

    /**
     * 提供（相对）精确的除法运算。当发生除不尽的情况时，由scale参数指
     * 定精度，以后的数字四舍五入。
     *
     * @param value1 被除数
     * @param value2 除数
     * @param scale  表示表示需要精确到小数点以后几位。
     * @return 两个参数的商
     */
    public static double divide(double value1, double value2, int scale) {
        if (scale < 0) {
            throw new CheckException("精度错误");
        }
        BigDecimal b1 = new BigDecimal(Double.toString(value1));
        BigDecimal b2 = new BigDecimal(Double.toString(value2));
        return b1.divide(b2, scale, BigDecimal.ROUND_HALF_UP).doubleValue();
    }

    /**
     * 提供精确的小数位四舍五入处理。
     *
     * @param v     需要四舍五入的数字
     * @param scale 小数点后保留几位
     * @return 四舍五入后的结果
     */
    public static double round(double v, int scale) {
        if (scale < 0) {
            throw new CheckException("精度错误");
        }
        BigDecimal b = new BigDecimal(Double.toString(v));
        BigDecimal one = new BigDecimal("1");
        return b.divide(one, scale, BigDecimal.ROUND_HALF_UP).doubleValue();
    }

    /**
     * 去掉特殊值 L
     *
     * @param valueStr 待转换字符
     * @return 处理后的字符
     */
    public static String removeSpecialSymbol(String valueStr) {
        //去掉L
        int dot = valueStr.lastIndexOf('L');
        if ((dot > -1) && (dot <= (valueStr.length() - 1))) {
            valueStr = valueStr.substring(0, dot);
        }
        return valueStr;
    }

    /**
     * 添加like词
     *
     * @param param 参数
     * @return "%" + param + "%"
     */
    public static String spliceLikeWord(String param) {
        return "%" + param + "%";
    }


    /**
     * 验证手机号的正确性
     *
     * @param phone 手机号
     */
    public static void isPhone(String phone) {
        String regex = "^((13[0-9])|(14[5,7,9])|(15([0-3]|[5-9]))|(166)|(17[0,1,3,5,6,7,8])|(18[0-9])|(19[8|9]))\\d{8}$";
        if (StringUtils.isBlank(phone) || phone.length() != 11) {
            throw new CheckException("手机号应为11位数");
        }

        Pattern p = Pattern.compile(regex);
        Matcher m = p.matcher(phone);
        boolean isMatch = m.matches();
        if (!isMatch) {
            throw new CheckException("手机号码格式不正确");
        }
    }
}
